import tkinter as tk
from tkinter import filedialog, messagebox
from pathlib import Path
import re
from datetime import datetime


class BatchRenameApp:
    """文件批量重命名工具"""
    
    def __init__(self, root):
        self.root = root
        self.root.title("文件批量重命名工具")
        self.root.geometry("700x600")
        self.root.resizable(False, False)
        
        # 居中显示窗口
        self.center_window()
        
        # 选中的文件夹路径和文件列表
        self.folder_path = None
        self.files = []
        
        # 创建UI
        self.create_widgets()
    
    def center_window(self):
        """使窗口居中显示"""
        self.root.update_idletasks()
        width = self.root.winfo_width()
        height = self.root.winfo_height()
        x = (self.root.winfo_screenwidth() // 2) - (width // 2)
        y = (self.root.winfo_screenheight() // 2) - (height // 2)
        self.root.geometry(f'{width}x{height}+{x}+{y}')
    
    def create_widgets(self):
        """创建界面组件"""
        # 文件夹选择框架
        folder_frame = tk.Frame(self.root, pady=10)
        folder_frame.pack(fill=tk.X, padx=20)
        
        self.folder_label = tk.Label(
            folder_frame,
            text="未选择文件夹",
            bg="#f0f0f0",
            anchor="w",
            padx=10,
            pady=10,
            relief=tk.SUNKEN
        )
        self.folder_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        select_btn = tk.Button(
            folder_frame,
            text="选择文件夹",
            command=self.select_folder,
            padx=15,
            pady=10,
            bg="#4CAF50",
            fg="black",
            font=("Arial", 10, "bold"),
            cursor="hand2"
        )
        select_btn.pack(side=tk.RIGHT, padx=(10, 0))
        
        # 重命名选项框架
        options_frame = tk.LabelFrame(
            self.root,
            text="重命名规则",
            font=("Arial", 11, "bold"),
            padx=15,
            pady=10
        )
        options_frame.pack(fill=tk.X, padx=20, pady=10)
        
        # 重命名模式选择
        mode_frame = tk.Frame(options_frame)
        mode_frame.pack(fill=tk.X, pady=5)
        
        tk.Label(mode_frame, text="重命名模式：", font=("Arial", 10)).pack(side=tk.LEFT)
        
        self.rename_mode = tk.StringVar(value="prefix")
        
        tk.Radiobutton(
            mode_frame,
            text="添加前缀",
            variable=self.rename_mode,
            value="prefix",
            font=("Arial", 9),
            command=self.update_preview
        ).pack(side=tk.LEFT, padx=10)
        
        tk.Radiobutton(
            mode_frame,
            text="添加后缀",
            variable=self.rename_mode,
            value="suffix",
            font=("Arial", 9),
            command=self.update_preview
        ).pack(side=tk.LEFT, padx=10)
        
        tk.Radiobutton(
            mode_frame,
            text="替换文本",
            variable=self.rename_mode,
            value="replace",
            font=("Arial", 9),
            command=self.update_preview
        ).pack(side=tk.LEFT, padx=10)
        
        tk.Radiobutton(
            mode_frame,
            text="序号命名",
            variable=self.rename_mode,
            value="number",
            font=("Arial", 9),
            command=self.update_preview
        ).pack(side=tk.LEFT, padx=10)
        
        # 输入框架
        input_frame = tk.Frame(options_frame)
        input_frame.pack(fill=tk.X, pady=10)
        
        # 第一个输入框
        self.input1_label = tk.Label(input_frame, text="前缀内容：", font=("Arial", 10), width=12, anchor="w")
        self.input1_label.grid(row=0, column=0, sticky="w", pady=5)
        
        self.input1_entry = tk.Entry(input_frame, font=("Arial", 10), width=30)
        self.input1_entry.grid(row=0, column=1, sticky="ew", pady=5)
        self.input1_entry.bind('<KeyRelease>', lambda e: self.update_preview())
        
        # 第二个输入框（替换模式用）
        self.input2_label = tk.Label(input_frame, text="替换为：", font=("Arial", 10), width=12, anchor="w")
        self.input2_label.grid(row=1, column=0, sticky="w", pady=5)
        
        self.input2_entry = tk.Entry(input_frame, font=("Arial", 10), width=30)
        self.input2_entry.grid(row=1, column=1, sticky="ew", pady=5)
        self.input2_entry.bind('<KeyRelease>', lambda e: self.update_preview())
        
        # 序号起始值（序号模式用）
        self.start_num_label = tk.Label(input_frame, text="起始序号：", font=("Arial", 10), width=12, anchor="w")
        self.start_num_label.grid(row=2, column=0, sticky="w", pady=5)
        
        self.start_num_entry = tk.Entry(input_frame, font=("Arial", 10), width=30)
        self.start_num_entry.insert(0, "1")
        self.start_num_entry.grid(row=2, column=1, sticky="ew", pady=5)
        self.start_num_entry.bind('<KeyRelease>', lambda e: self.update_preview())
        
        input_frame.columnconfigure(1, weight=1)
        
        # 初始化输入框显示
        self.update_input_labels()
        
        # 预览框架
        preview_frame = tk.LabelFrame(
            self.root,
            text="重命名预览（前10个文件）",
            font=("Arial", 10, "bold"),
            padx=10,
            pady=10
        )
        preview_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
        
        # 预览列表
        preview_container = tk.Frame(preview_frame)
        preview_container.pack(fill=tk.BOTH, expand=True)
        
        scrollbar = tk.Scrollbar(preview_container)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.preview_text = tk.Text(
            preview_container,
            yscrollcommand=scrollbar.set,
            font=("Courier", 9),
            height=10,
            bg="#f9f9f9"
        )
        self.preview_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.preview_text.yview)
        
        # 状态标签
        self.status_label = tk.Label(
            self.root,
            text="请选择包含文件的文件夹",
            font=("Arial", 9),
            fg="#666"
        )
        self.status_label.pack(pady=(0, 10))
        
        # 重命名按钮
        self.rename_btn = tk.Button(
            self.root,
            text="开始重命名",
            command=self.start_rename,
            padx=30,
            pady=12,
            bg="#1976D2",
            fg="black",
            font=("Arial", 11, "bold"),
            cursor="hand2",
            state=tk.DISABLED
        )
        self.rename_btn.pack(pady=(0, 20))
    
    def update_input_labels(self):
        """根据重命名模式更新输入框标签"""
        mode = self.rename_mode.get()
        
        if mode == "prefix":
            self.input1_label.config(text="前缀内容：")
            self.input2_label.grid_remove()
            self.input2_entry.grid_remove()
            self.start_num_label.grid_remove()
            self.start_num_entry.grid_remove()
        elif mode == "suffix":
            self.input1_label.config(text="后缀内容：")
            self.input2_label.grid_remove()
            self.input2_entry.grid_remove()
            self.start_num_label.grid_remove()
            self.start_num_entry.grid_remove()
        elif mode == "replace":
            self.input1_label.config(text="查找文本：")
            self.input2_label.grid()
            self.input2_entry.grid()
            self.start_num_label.grid_remove()
            self.start_num_entry.grid_remove()
        elif mode == "number":
            self.input1_label.config(text="文件名前缀：")
            self.input2_label.grid_remove()
            self.input2_entry.grid_remove()
            self.start_num_label.grid()
            self.start_num_entry.grid()
    
    def select_folder(self):
        """选择文件夹"""
        folder = filedialog.askdirectory(title="选择包含文件的文件夹")
        if folder:
            self.folder_path = folder
            self.folder_label.config(text=folder)
            self.scan_files()
    
    def scan_files(self):
        """扫描文件夹中的文件"""
        if not self.folder_path:
            return
        
        folder = Path(self.folder_path)
        self.files = sorted([f for f in folder.iterdir() if f.is_file()])
        
        if self.files:
            self.status_label.config(
                text=f"检测到 {len(self.files)} 个文件",
                fg="#4CAF50"
            )
            self.rename_btn.config(state=tk.NORMAL)
            self.update_preview()
        else:
            self.status_label.config(
                text="文件夹中没有文件",
                fg="#F44336"
            )
            self.rename_btn.config(state=tk.DISABLED)
            self.preview_text.delete(1.0, tk.END)
    
    def generate_new_name(self, file_path, index):
        """根据规则生成新文件名"""
        mode = self.rename_mode.get()
        old_name = file_path.stem
        extension = file_path.suffix
        
        if mode == "prefix":
            prefix = self.input1_entry.get()
            new_name = f"{prefix}{old_name}{extension}"
        
        elif mode == "suffix":
            suffix = self.input1_entry.get()
            new_name = f"{old_name}{suffix}{extension}"
        
        elif mode == "replace":
            find_text = self.input1_entry.get()
            replace_text = self.input2_entry.get()
            new_name = f"{old_name.replace(find_text, replace_text)}{extension}"
        
        elif mode == "number":
            prefix = self.input1_entry.get()
            try:
                start_num = int(self.start_num_entry.get())
            except ValueError:
                start_num = 1
            number = start_num + index
            # 自动补零，根据总文件数决定位数
            width = len(str(start_num + len(self.files)))
            new_name = f"{prefix}{str(number).zfill(width)}{extension}"
        
        else:
            new_name = file_path.name
        
        return new_name
    
    def update_preview(self):
        """更新预览"""
        self.update_input_labels()
        
        if not self.files:
            return
        
        self.preview_text.delete(1.0, tk.END)
        
        # 显示前10个文件的预览
        preview_count = min(10, len(self.files))
        
        for i in range(preview_count):
            file = self.files[i]
            old_name = file.name
            new_name = self.generate_new_name(file, i)
            
            self.preview_text.insert(tk.END, f"{old_name}\n")
            self.preview_text.insert(tk.END, f"  →  {new_name}\n\n")
        
        if len(self.files) > 10:
            self.preview_text.insert(tk.END, f"... 还有 {len(self.files) - 10} 个文件")
    
    def start_rename(self):
        """开始重命名"""
        if not self.files:
            return
        
        result = messagebox.askyesno(
            "确认重命名",
            f"确定要重命名 {len(self.files)} 个文件吗？\n\n"
            "此操作不可撤销，建议先备份文件！"
        )
        
        if result:
            self.rename_files()
    
    def rename_files(self):
        """执行重命名"""
        try:
            self.status_label.config(text="正在重命名...", fg="#2196F3")
            self.rename_btn.config(state=tk.DISABLED)
            
            renamed_count = 0
            errors = []
            
            for i, file in enumerate(self.files):
                try:
                    new_name = self.generate_new_name(file, i)
                    new_path = file.parent / new_name
                    
                    # 如果新文件名已存在且不是当前文件，跳过
                    if new_path.exists() and new_path != file:
                        errors.append(f"{file.name} -> 目标文件已存在")
                        continue
                    
                    # 重命名
                    file.rename(new_path)
                    renamed_count += 1
                    
                except Exception as e:
                    errors.append(f"{file.name} -> {str(e)}")
                    continue
            
            # 显示结果
            self.rename_complete(renamed_count, errors)
            
        except Exception as e:
            self.rename_error(str(e))
    
    def rename_complete(self, renamed_count, errors):
        """重命名完成"""
        self.status_label.config(
            text=f"重命名完成！成功 {renamed_count} 个",
            fg="#4CAF50"
        )
        self.rename_btn.config(state=tk.NORMAL)
        
        message = f"重命名完成！\n\n成功重命名：{renamed_count} 个文件"
        
        if errors:
            message += f"\n失败：{len(errors)} 个文件\n\n错误详情：\n"
            message += "\n".join(errors[:5])
            if len(errors) > 5:
                message += f"\n... 还有 {len(errors) - 5} 个错误"
        
        messagebox.showinfo("重命名完成", message)
        
        # 重新扫描
        self.scan_files()
    
    def rename_error(self, error_msg):
        """重命名出错"""
        self.status_label.config(
            text="重命名失败",
            fg="#F44336"
        )
        self.rename_btn.config(state=tk.NORMAL)
        
        messagebox.showerror(
            "重命名失败",
            f"重命名文件时出错：\n{error_msg}"
        )


def main():
    """主函数"""
    root = tk.Tk()
    app = BatchRenameApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()
